package com.superman.libyuvdemo

import android.graphics.BitmapFactory
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaCodecList
import android.media.MediaFormat
import android.media.MediaMuxer
import android.opengl.Matrix
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import com.superman.media.es.TextureRender
import java.io.File
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.concurrent.thread

class EncodeUsingEGLAndSurfaceActivity : ComponentActivity() {

    private val TAG = EncodeUsingEGLAndSurfaceActivity::class.qualifiedName
    private val viewportWidth = 1280
    private val viewportHeight = 720
    private val FRAME_RATE = 30
    private var inputFrameIndex = 0
    private val outputEnd = AtomicBoolean(false)
    private var muxer: MediaMuxer? = null
    private var isMuxerStarted = false
    private var videoTrackIndex = -1
    private var encodedFrameIndex = 0
    private val NUM_FRAMES = FRAME_RATE * 5


    val exe = Executors.newFixedThreadPool(1)
    var s: EncodeSurface? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {

            Column {

                Button(onClick = {
                    thread {
                        encodeUsingEGLAndSurface()
                    }
                }) {
                    Text(text = "start")
                }

                Button(onClick = {
                   thread{
                        s = EncodeSurface(this@EncodeUsingEGLAndSurfaceActivity)
                        s?.encodeUsingEGLAndSurface()
                        Thread.sleep(1000)

                        repeat(160){
                            val options = BitmapFactory.Options()
                            options.inScaled = false
                            val bitmap =
                                BitmapFactory.decodeResource(resources, R.raw.nihoa, options)
                            s?.draw(bitmap)

                        }
                    }



                }) {
                    Text(text = "start222")
                }

            }
        }
    }

    private fun encodeUsingEGLAndSurface() {
        val mimeType = MediaFormat.MIMETYPE_VIDEO_AVC
        val format = MediaFormat.createVideoFormat(mimeType, viewportWidth, viewportHeight)
        val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
        val encodeCodecName = codecList.findEncoderForFormat(format)
        val encoder = MediaCodec.createByCodecName(encodeCodecName)

        encoder.setCallback(object : MediaCodec.Callback() {
            override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {

            }

            override fun onOutputBufferAvailable(
                codec: MediaCodec,
                index: Int,
                info: MediaCodec.BufferInfo
            ) {
//                Log.d(TAG, "encoder output buffer available:$index")
                val isDone = (info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0
                if (isDone || encodedFrameIndex == NUM_FRAMES) {
                    outputEnd.set(true)
                    info.size = 0
                    return
                }
                if (info.size > 0) {
                    if (!isMuxerStarted) {
                        throw RuntimeException("muxer hasn't started");
                    }
                    val encodedData = codec.getOutputBuffer(index)
                    encodedData?.position(info.offset)
                    encodedData?.limit(info.offset + info.size)

                    muxer?.writeSampleData(videoTrackIndex, encodedData!!, info)
                    Log.d(
                        TAG,
                        "encoder output buffer:$index,size:${info.size},pts:${info.presentationTimeUs}"
                    )

                    codec.releaseOutputBuffer(index, false)
                    ++encodedFrameIndex
                }
            }

            override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
                e.printStackTrace()
            }

            override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {
                Log.d(TAG, "encoder output format changed:$format")
                videoTrackIndex = muxer?.addTrack(format)!!
                muxer?.start()
                isMuxerStarted = true
            }

        })
        Log.d(TAG, "codec info:" + encoder.codecInfo)
        val colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat)
        format.setInteger(MediaFormat.KEY_BIT_RATE, 2_000_000)
        format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE)
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
        Log.d(TAG, "format:$format")
        encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

        val inputSurface = com.superman.media.es.InputSurface(encoder.createInputSurface())
        inputSurface.makeCurrent()

        val render = TextureRender()

        val outputDir = cacheDir
        val outputName = "name.mp4"
        val outputFile = File(outputDir, outputName)
        muxer = MediaMuxer(outputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)

        encoder.start()

        val options = BitmapFactory.Options()
        options.inScaled = false
        val bitmap = BitmapFactory.decodeResource(resources, R.raw.nihoa, options)

        while (!outputEnd.get()) {
            render.draw(viewportWidth, viewportHeight, bitmap, getMvp())
            val nanoPts = computePresentationTime(inputFrameIndex) * 1000
            inputSurface.setPresentationTime(nanoPts)
            inputSurface.swapBuffers()
            Thread.sleep(10)
            inputFrameIndex++
        }

        encoder.stop()
        muxer?.stop()
        encoder.release()
    }

    private fun computePresentationTime(frameIndex: Int): Long {
        val t = (frameIndex * 1000_000 / FRAME_RATE).toLong()
        return t
    }

    fun getMvp(): FloatArray {
        val mvp = FloatArray(16)
        Matrix.setIdentityM(mvp, 0)
        Matrix.scaleM(mvp, 0, 1f, -1f, 1f)
        return mvp
    }

}